0129. Number 类型的取值范围【扩展】
- 1. 🎯 本节内容
- 2. 🫧 评价
- 3. 🤔 JS 中的整数是 32 位吗?
- 4. 🤔 “整数安全范围”是
(-2^63, 2^63 - 1)吗? - 5. 🤔 为什么 JS 中整数的安全范围是
(-(2^53 - 1))到(2^53 - 1)? - 6. 🤔 为什么有些时候会说 JS 中的整数是 32 位的呢?
- 7. 🤔 位运算的时候会自动隐式转换,这有官方依据吗?
- 8. 🔗 引用
1. 🎯 本节内容
- 数字类型的安全范围
- 数字类型在位运算时的自动隐式转换
2. 🫧 评价
- 在刷 leetcode 的时候,好多地方(跟位运算相关的大多数题目)都用到了“JS 整数是 32 位”的这个特性来求解,于是查了一下 JS 中整数的范围问题。
- 记住结论:
- 虽然 JS 的 Number 是 64 位浮点数,但在位运算中,JS 会把它强制转换成 32 位有符号整数,只保留低 32 位,高位舍弃。
- JS 的整数安全范围是
到 ,因为Number类型基于 IEEE 754 双精度浮点数,其尾数精度只有 53 位,超过这个范围就会丢失整数精度。
3. 🤔 JS 中的整数是 32 位吗?
- JS 的数值类型是 64 位浮点数(IEEE 754 双精度浮点数),不是 32 位整数。
- 但按位运算会转成 32 位有符号整数。
4. 🤔 “整数安全范围”是 (-2^63, 2^63 - 1) 吗?
- 不是哦 🙂
- 虽然 Number 是 64 位浮点数,但它只能安全表示:
- 也就是:
-(9007199254740991) ~ 9007199254740991, 超过这个范围的整数,Number就无法保证逐个整数都能精确表示,会出现“跳数”。
js
Number.MIN_SAFE_INTEGER // === -9007199254740991 === -(Math.pow(2, 53) - 1)
Number.MAX_SAFE_INTEGER - // === 9007199254740991 === (Math.pow(2, 53) - 1)1
2
2
- 如果真要“大整数”
- ES2020 引入了 BigInt,能表示任意精度的整数:
js
const big = 123456789012345678901234567890n1
(-2^63, 2^63 - 1)这个范围对应的是 64 位整型的数学取值范围,更准确地说:0到2^64 - 1是无符号 64 位整数的范围;-2^63到2^63 - 1是有符号 64 位整数的范围);
- JavaScript 的
Number并不是 64 位整数,而是浮点数。
5. 🤔 为什么 JS 中整数的安全范围是 (-(2^53 - 1)) 到 (2^53 - 1)?
- 根源在于 IEEE 754 双精度浮点数 (double precision floating point) 的存储方式。
- Number 的底层结构(64 位浮点数)
- 一个 JS 的
Number用 64 位二进制存储:- 1 位:符号位 (sign)
- 11 位:指数位 (exponent)
- 52 位:尾数位 (fraction / mantissa)
- 但注意:尾数存储时有一个 隐含的最高有效位(hidden bit),所以尾数实际能表示 53 位精度。
- mantissa 有 52 位存储空间 + 1 位隐含的 leading 1。
- 在整数范围内,浮点数最多能精确区分到
以内的每一个整数。
- 一个 JS 的
- 超过 53 位就不安全了
- 一旦整数大于
,可用的精度不足,浮点数就“跳着”表示整数,不能保证每个整数都能唯一、精确表示。
- 一旦整数大于
js
console.log(2 ** 53) // 9007199254740992
console.log(2 ** 53 + 1) // 9007199254740992
console.log(2 ** 53 + 2) // 9007199254740994
// 会发现 2**53 和 2**53 + 1 变成了同一个值。1
2
3
4
5
2
3
4
5
- 实际运行结果:
6. 🤔 为什么有些时候会说 JS 中的整数是 32 位的呢?
- 有些操作会把
Number当作 32 位有符号整数(int32) 来处理: - 按位运算符(
& | ^ ~ << >> >>>) - 它们会先把
Number转换为 32 位有符号整数,再运算。 - 比如:
js
1.9 | 0 // => 1
2147483648 | 0 // => -2147483648 (溢出了)1
2
2
- 所以按位运算的结果总在
[-2^31, 2^31-1]范围内。
javascript
/**
* @param {number} left
* @param {number} right
* @return {number}
*/
var countPrimeSetBits = function (left, right) {
// 在32位整数范围内,1的个数最多为32,预先定义可能的质数集合
const primes = new Set([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31])
let result = 0
for (let i = left; i <= right; i++) {
// 使用内置函数计算1的个数
const onesCount = i.toString(2).split('1').length - 1
if (primes.has(onesCount)) {
result++
}
}
return result
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 这题其实就是利用了 JS 按位运算“限制在 32 位整数”的事实。
- JS 的整数在 按位操作时会被截断成 32 位有符号整数。
7. 🤔 位运算的时候会自动隐式转换,这有官方依据吗?
- 🔎 官方 ecma262 截图
6.1.6.1.16 NumberBitwiseOp ( op, x, y )这一部分讲解的就是数字的位运算,里边儿明显提到,会先对操作数 x 和 y 执行ToInt32转换。ToInt32的定义在7.1.6 ToInt32 (argument):
- 这一部分详细描述了
ToInt32(argument)这个方法将操作数 argument 转换为 32 位带符号整数的具体规则。 - 它清楚地列出了转换的每一个步骤:
- 先将参数转为数字(
ToNumber)。 - 处理
NaN和无穷大。 - 截断小数部分(
truncate),这是将浮点数转换为整数的关键一步。 - 进行取模和符号调整,确保结果在 32 位整数的有效范围内。
- 先将参数转为数字(
- 示例:
js
console.log(4294967295 | 0) // -1
// 4294967295 超过了 32 位无符号范围
// 转成 int32 就是 11111111111111111111111111111111 (32 个 1)
// 按二进制补码解释就是 -1
console.log(3.9 | 0) // 3
// 先 ToInt32:浮点数小数部分会被截断1
2
3
4
5
6
7
2
3
4
5
6
7
- 综上,“位运算会隐式把 Number 转成 32 位有符号整数” 不是经验结论,而是写进规范的明确规则。

